import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:kq_flutter_core_widget/widgets/chart/ex/extension.dart';
import 'package:kq_flutter_core_widget/widgets/chart/axis/combined/combined_entity.dart';
import 'package:kq_flutter_core_widget/widgets/chart/axis/x_axis.dart';
import 'package:kq_flutter_core_widget/widgets/chart/axis/y_axis.dart';
import 'package:kq_flutter_core_widget/widgets/chart/axis/base/axis.dart'
    as axis;
import 'package:kq_flutter_pad_widgets/widgets/chart/pad_chart/axis/bar/kq_pad_bar_chart.dart';

import '../../base/pad_base_chart.dart';
import '../base/pad_base_axis_chart.dart';
import '../line/pad_line_chart.dart';

/// 组合图代理
///
/// 目前只支持柱状图和折线图的组合
///
/// Created by wanggaowan on 2023/12/6 17:54
class KqPadCombinedChartDelegate
    extends PadBaseAxisChartDelegate<CombinedData> {
  /// 构建组合图
  ///
  /// [mainChart]表示当前组合图以哪个图表作为主图表，主图表负责X轴，Y轴绘制，其它图表只负责自身的数据绘制及手势处理
  /// [xAxis]配置x轴相关属性，[xAxisRender]则负责x轴的绘制。
  /// [yAxis]配置y轴相关属性，[yAxisRender]则负责y轴的绘制。
  /// [dataRender]负责将组合图数据绘制到界面及高亮数据绘制。
  /// [gestureHandler]为手势处理器，处理图表的点击，滑动等操作。
  /// [emptyWidgetBuilder]为无数据时构建需要显示的内容。
  /// [isEmptyData]用于判断给得的数据是否为空，从而决定是否调用[emptyWidgetBuilder]绘制空数据界面。
  /// [animDuration]为动画时间，此值大于0则绘制时伴随动画
  KqPadCombinedChartDelegate(
      {PadCombinedChartDataRender? dataRender,
      XAxis? xAxis,
      YAxis? yAxis,
      this.yAxisEnd,
      PadCombinedXAxisRender? xAxisRender,
      PadCombinedYAxisRender? yAxisRender,
      PadCombinedCharGestureHandler? gestureHandler,
      this.mainChart = ChartType.bar,
      super.animDuration,
      super.data,
      super.emptyWidgetBuilder,
      super.isDataEmpty})
      : super(
          dataRender: dataRender ?? PadCombinedChartDataRender(),
          xAxis: xAxis ?? XAxis(),
          yAxis: yAxis ?? YAxis(),
          xAxisRender: xAxisRender ?? PadCombinedXAxisRender(),
          yAxisRender: yAxisRender ?? PadCombinedYAxisRender(),
          gestureHandler: gestureHandler,
        ) {
    _barChartDelegate = KqPadBarChartDelegate(
        xAxis: this.xAxis,
        yAxis:
            data?.barData?.yAxisType != YAxisType.end ? this.yAxis : yAxisEnd!,
        data: data?.barData?.data,
        dataRender: this.dataRender.barChartDataRender,
        gestureHandler: this.gestureHandler?.barCharGestureHandler);
    _lineChartDelegate = KqPadLineChartDelegate(
        xAxis: this.xAxis,
        yAxis:
            data?.lineData?.yAxisType != YAxisType.end ? this.yAxis : yAxisEnd!,
        data: data?.lineData?.data,
        dataRender: this.dataRender.lineChartDataRender,
        gestureHandler: this.gestureHandler?.lineCharGestureHandler);
  }

  /// 构建柱状图，对非必传的功能提供默认实现
  ///
  /// [mainChart]表示当前组合图以哪个图表作为主图表，主图表负责X轴，Y轴绘制，其它图表只负责自身的数据绘制及手势处理
  /// [xAxis]配置x轴相关属性，[xAxisRender]则负责x轴的绘制。
  /// [yAxis]配置y轴相关属性，[yAxisRender]则负责y轴的绘制。
  /// [dataRender]负责将组合图数据绘制到界面及高亮数据绘制。
  /// [gestureHandler]为手势处理器，处理图表的点击，滑动等操作。
  /// [emptyWidgetBuilder]为无数据时构建需要显示的内容。
  /// [isEmptyData]用于判断给得的数据是否为空，从而决定是否调用[emptyWidgetBuilder]绘制空数据界面。
  /// [animDuration]为动画时间，此值大于0则绘制时伴随动画
  KqPadCombinedChartDelegate.withDefault(
      {ChartType mainChart = ChartType.bar,
      XAxis? xAxis,
      YAxis? yAxis,
      YAxis? yAxisEnd,
      PadCombinedChartDataRender? dataRender,
      PadCombinedXAxisRender? xAxisRender,
      PadCombinedYAxisRender? yAxisRender,
      Duration? animDuration,
      CombinedData? data,
      PadCombinedCharGestureHandler? gestureHandler,
      Widget Function()? emptyWidgetBuilder,
      bool Function(CombinedData? data)? isDataEmpty})
      : this(
            mainChart: mainChart,
            xAxis: xAxis,
            yAxis: yAxis,
            yAxisEnd: yAxisEnd,
            dataRender: dataRender,
            xAxisRender: xAxisRender,
            yAxisRender: yAxisRender,
            animDuration: animDuration,
            data: data,
            gestureHandler: gestureHandler ??
                PadCombinedCharGestureHandler(
                    lineCharGestureHandler: PadLineCharGestureHandler(),
                    barCharGestureHandler: PadBarCharGestureHandler()),
            emptyWidgetBuilder: emptyWidgetBuilder ?? getDefEmptyView,
            isDataEmpty: isDataEmpty);

  late final KqPadBarChartDelegate _barChartDelegate;
  late final KqPadLineChartDelegate _lineChartDelegate;
  final YAxis? yAxisEnd;
  final ChartType mainChart;

  @override
  void attachChart(PadChartStateMixin state) {
    super.attachChart(state);
    _barChartDelegate.attachChart(state);
    _lineChartDelegate.attachChart(state);
  }

  @override
  void dispose() {
    super.dispose();
    _barChartDelegate.dispose();
    _lineChartDelegate.dispose();
  }

  @override
  XAxis get xAxis => super.xAxis as XAxis;

  @override
  YAxis get yAxis => super.yAxis as YAxis;

  @override
  PadCombinedChartDataRender get dataRender =>
      super.dataRender as PadCombinedChartDataRender;

  @override
  PadCombinedCharGestureHandler? get gestureHandler =>
      super.gestureHandler as PadCombinedCharGestureHandler?;

  @override
  PadXAxisRender get xAxisRender => super.xAxisRender as PadXAxisRender;

  @override
  PadCombinedYAxisRender get yAxisRender =>
      super.yAxisRender as PadCombinedYAxisRender;

  @override
  bool get isEmptyData {
    var isDataEmptyFunc = isDataEmpty;
    if (isDataEmptyFunc != null) {
      return isDataEmptyFunc.call(data);
    }

    if (data == null) {
      return true;
    }

    return _lineChartDelegate.isEmptyData && _barChartDelegate.isEmptyData;
  }

  @override
  void didUpdateWidget(covariant PadBaseChart oldWidget) {
    super.didUpdateWidget(oldWidget);
    _updateGestureHandler(oldWidget);
  }

  void _updateGestureHandler(PadBaseChart oldWidget) {
    var gestureHandler = this.gestureHandler;
    if (gestureHandler == null) {
      return;
    }

    var oldGestureHandler = oldWidget.delegate.gestureHandler;
    if (oldGestureHandler is! PadCombinedCharGestureHandler) {
      return;
    }

    gestureHandler.barCharGestureHandler
        ?.update(oldGestureHandler.barCharGestureHandler);
    gestureHandler.lineCharGestureHandler
        ?.update(oldGestureHandler.lineCharGestureHandler);
  }

  @override
  void onDraw(Canvas canvas, double animProgress, bool isDrawX, bool isDrawY,
      bool canScroll) {
    initAxisLabel(yAxis);
    initAxisLabel(xAxis);
    if (yAxisEnd != null) {
      initAxisLabel(yAxisEnd!);
    }

    // 画布的开始和结束坐标
    double start = 0;
    double top = 0;
    double bottom = size.height;
    double end = size.width;

    // y轴绘制文本的最大宽度
    var yMaxTextWidth = getYAxisTextMaxWidth(yAxis);
    var yMaxTextWidthEnd =
        yAxisEnd != null ? getYAxisTextMaxWidth(yAxisEnd!) : 0.0;
    // x轴轴线左边起始位置
    var xAxisStart = start + yMaxTextWidth + yAxis.labelOffsetAxis;
    // x轴轴线右边结束位置
    var xAxisEnd = end -
        yMaxTextWidthEnd -
        (yAxisEnd == null ? 0 : yAxisEnd!.labelOffsetAxis);

    // 网格宽度，x轴文本绘制的最大宽度
    var gridWidth = getGridWidth(
      xAxis.labelCount,
      xAxisEnd -
          xAxisStart -
          yAxis.lineWidth -
          xAxis.startPadding -
          xAxis.endPadding,
    );

    var xLabelMaxHeight = xAxisRender.onDraw(
        this,
        canvas,
        ui.Rect.fromLTRB(
          xAxisStart,
          top,
          xAxisEnd,
          bottom,
        ),
        gridWidth);

    // x轴轴线以下部分绘制内容高度结束位置
    var xAxisBottom = bottom - xLabelMaxHeight;
    //  x轴轴线y轴坐标
    var xAxisTop = xAxisBottom - xAxis.lineWidth;
    // 网格高度，y轴文本绘制的最大高度，
    var gridHeight = getGridHeight(yAxis.labelCount,
        xAxisTop - top - yAxis.startPadding - yAxis.endPadding);

    yAxisRender.onDraw(
        this,
        canvas,
        ui.Rect.fromLTRB(
            xAxisStart, top, (xAxisStart + yAxis.lineWidth), xAxisTop),
        gridHeight,
        yMaxTextWidth,
        xAxisEnd - xAxisStart);

    if (yAxisEnd != null) {
      yAxisRender.onDrawEnd(
          this,
          canvas,
          ui.Rect.fromLTRB(
              xAxisEnd - yAxisEnd!.lineWidth, top, xAxisEnd, xAxisTop),
          gridHeight,
          yMaxTextWidthEnd,
          xAxisEnd - xAxisStart);
    }

    dataRender.onDraw(
        this,
        canvas,
        ui.Rect.fromLTRB(
            xAxisStart + xAxis.startPadding + yAxis.lineWidth,
            top + yAxis.endPadding,
            xAxisEnd -
                xAxis.endPadding -
                (yAxisEnd == null ? 0 : yAxisEnd!.lineWidth),
            xAxisTop - yAxis.startPadding),
        gridWidth,
        gridHeight,
        animProgress);
  }

  @override
  void initAxisLabel(axis.Axis axis) {
    if (mainChart == ChartType.bar) {
      _barChartDelegate.initAxisLabel(axis);
    } else {
      _lineChartDelegate.initAxisLabel(axis);
    }
  }

  @override
  double getGridWidth(int labelCount, double xAxisLength) {
    if (mainChart == ChartType.bar) {
      return _barChartDelegate.getGridWidth(labelCount, xAxisLength);
    } else {
      return _lineChartDelegate.getGridWidth(labelCount, xAxisLength);
    }
  }

  @override
  double getGridHeight(int labelCount, double yAxisLength) {
    if (mainChart == ChartType.bar) {
      return _barChartDelegate.getGridHeight(labelCount, yAxisLength);
    } else {
      return _lineChartDelegate.getGridHeight(labelCount, yAxisLength);
    }
  }
}

class PadCombinedCharGestureHandler
    with PadBaseChartGestureHandlerMixin<KqPadCombinedChartDelegate> {
  const PadCombinedCharGestureHandler(
      {this.barCharGestureHandler,
      this.lineCharGestureHandler,
      this.tapEnable = true,
      this.dragEnable = true});

  @override
  final bool tapEnable;
  @override
  final bool dragEnable;
  final PadBarCharGestureHandler? barCharGestureHandler;
  final PadLineCharGestureHandler? lineCharGestureHandler;

  @override
  void attachChart(KqPadCombinedChartDelegate delegate) {
    barCharGestureHandler?.attachChart(delegate._barChartDelegate);
    lineCharGestureHandler?.attachChart(delegate._lineChartDelegate);
  }

  @override
  void dispose() {
    barCharGestureHandler?.dispose();
    lineCharGestureHandler?.dispose();
  }

  @override
  void onTapDown(DragDownDetails details) {
    barCharGestureHandler?.onTapDown(details);
    lineCharGestureHandler?.onTapDown(details);
  }

  @override
  void onTapUp(DragDownDetails details) {
    barCharGestureHandler?.onTapUp(details);
    lineCharGestureHandler?.onTapUp(details);
  }

  @override
  void onDragEnd(DragEndDetails details) {
    barCharGestureHandler?.onDragEnd(details);
    lineCharGestureHandler?.onDragEnd(details);
  }

  @override
  void onDragStart(DragStartDetails details) {
    barCharGestureHandler?.onDragStart(details);
    lineCharGestureHandler?.onDragStart(details);
  }

  @override
  void onDragUpdate(DragUpdateDetails details) {
    barCharGestureHandler?.onDragUpdate(details);
    lineCharGestureHandler?.onDragUpdate(details);
  }
}

class PadCombinedChartDataRender
    with PadBaseDataRenderMixin<KqPadCombinedChartDelegate> {
  PadCombinedChartDataRender(
      {this.lineChartDataRender, this.barChartDataRender});

  final PadLineChartDataRender? lineChartDataRender;
  final PadBarChartDataRender? barChartDataRender;

  @override
  void onDraw(KqPadCombinedChartDelegate chart, Canvas canvas, Rect rect,
      double gridWidth, double gridHeight, double animProgress) {
    var data = chart.data;
    if (data == null) {
      return;
    }

    for (var value in data.drawOrder) {
      double mapGridWidth;
      double mapGridHeight;
      if (value == ChartType.bar) {
        if (chart.mainChart == ChartType.bar) {
          mapGridWidth = gridWidth;
          mapGridHeight = gridHeight;
        } else {
          // 主轴是折线图，此时柱状图网格为标签数 - 1
          var count = chart.xAxis.labelCount;
          mapGridWidth =
              chart._barChartDelegate.getGridWidth(count - 1, rect.width);
          mapGridHeight =
              chart._barChartDelegate.getGridHeight(count - 1, rect.height);
        }

        chart._barChartDelegate.dataRender.onDraw(
            chart._barChartDelegate,
            canvas,
            rect,
            mapGridWidth,
            mapGridHeight,
            data.barData?.drawWithAnim == true ? animProgress : 1);
      } else {
        if (chart.mainChart == ChartType.line) {
          mapGridWidth = gridWidth;
          mapGridHeight = gridHeight;
        } else {
          // 主轴是柱状图，此时折线图网格为标签数 + 1
          var count = chart.xAxis.labelCount;
          mapGridWidth =
              chart._lineChartDelegate.getGridWidth(count + 1, rect.width);
          mapGridHeight =
              chart._lineChartDelegate.getGridHeight(count + 1, rect.height);
        }

        chart._lineChartDelegate.dataRender.onDraw(
            chart._lineChartDelegate,
            canvas,
            rect,
            mapGridWidth,
            mapGridHeight,
            data.lineData?.drawWithAnim == true ? animProgress : 1);
      }
    }
  }
}

class PadCombinedXAxisRender<C extends KqPadCombinedChartDelegate>
    extends PadXAxisRender<C> {
  @override
  double onDraw(C chart, ui.Canvas canvas, ui.Rect rect, double gridWidth) {
    if (chart.mainChart == ChartType.bar) {
      return chart._barChartDelegate.xAxisRender
          .onDraw(chart, canvas, rect, gridWidth);
    } else {
      return chart._lineChartDelegate.xAxisRender
          .onDraw(chart, canvas, rect, gridWidth);
    }
  }
}

class PadCombinedYAxisRender<C extends KqPadCombinedChartDelegate>
    extends PadYAxisRender<C> {
  void onDrawEnd(C chart, ui.Canvas canvas, ui.Rect rect, double gridHeight,
      double labelMaxWidth, double xAxisRange) {
    var yAxis = chart.yAxisEnd;
    if (yAxis == null) {
      return;
    }

    var textPaint = TextPainter(textDirection: TextDirection.ltr);
    var paint = Paint();
    var path = Path();

    // 绘制轴线
    var xAxis = chart.xAxis;
    if (yAxis.lineWidth > 0) {
      paint.color = yAxis.lineColor;
      paint.strokeWidth = yAxis.lineWidth;
      paint.style = PaintingStyle.stroke;
      var x = rect.left + (rect.right - rect.left) / 2;
      path.moveTo(x, rect.top);
      path.lineTo(x, rect.bottom);
      if (yAxis.lineDash != null) {
        path = path.dashPath(yAxis.lineDash!);
      }
      canvas.drawPath(path, paint);
    }
    yAxis.$drawRect = rect;

    var labelList = yAxis.labelList;
    if (labelList == null || labelList.isEmpty) {
      return;
    }

    // 绘制轴线文本
    var yLineDrawBottom = rect.bottom - yAxis.startPadding;
    paint.color = yAxis.gridLineColor;
    paint.strokeWidth = yAxis.gridLineWidth;
    paint.style = PaintingStyle.stroke;
    int labelCount = labelList.length;
    for (int i = 0; i < labelCount; i++) {
      if (gridHeight > 0 && labelMaxWidth > 0) {
        if (isDrawLabelByGridLine) {
          drawLabelByGridLine(chart, canvas, textPaint, i, labelList, yAxis,
              gridHeight, rect.left, labelMaxWidth, yLineDrawBottom, true);
        } else {
          drawLabelByGridCenter(canvas, textPaint, i, labelList, yAxis,
              gridHeight, rect.left, labelMaxWidth, yLineDrawBottom, true);
        }
      }

      if (i > 0 && yAxis.gridLineWidth > 0) {
        var startX = yAxis.gridUseStartPadding
            ? rect.right - xAxisRange + xAxis.startPadding
            : rect.right - xAxisRange;
        if (yAxis.gridLineWidth > 0 && i == labelCount - 1) {
          // 由Y轴补全X轴Y轴交叉点缺失部分，如果yAxis.gridUseEndPadding为true，则Y轴结束点实际都在X轴最后一根轴线左边
          startX += yAxis.gridLineWidth / 2;
        }

        path.reset();
        path.moveTo(
            yAxis.gridUseEndPadding
                ? rect.right - xAxis.endPadding
                : rect.right,
            yLineDrawBottom);
        path.lineTo(startX, yLineDrawBottom);
        if (yAxis.gridLineDash != null) {
          path = path.dashPath(yAxis.gridLineDash!);
        }

        canvas.drawPath(path, paint);
      }

      yLineDrawBottom -= gridHeight;
    }

    if (!isDrawLabelByGridLine && yAxis.gridLineWidth > 0) {
      var startX = yAxis.gridUseStartPadding
          ? rect.right - xAxisRange + xAxis.startPadding
          : rect.right - xAxisRange;
      if (yAxis.gridLineWidth > 0) {
        // 由Y轴补全X轴Y轴交叉点缺失部分，如果yAxis.gridUseEndPadding为true，则Y轴结束点实际都在X轴最后一根轴线左边
        startX += yAxis.gridLineWidth / 2;
      }

      path.reset();
      path.moveTo(
          yAxis.gridUseEndPadding ? rect.right - xAxis.endPadding : rect.right,
          yLineDrawBottom);
      path.lineTo(startX, yLineDrawBottom);
      if (yAxis.gridLineDash != null) {
        path = path.dashPath(yAxis.gridLineDash!);
      }

      canvas.drawPath(path, paint);
    }

    textPaint.dispose();
  }
}
